package at.ac.tuwien.prog.compiler;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import at.ac.tuwien.prog.Util;
import at.ac.tuwien.prog.command.CommandConstants;
import at.ac.tuwien.prog.command.ICommand;
import at.ac.tuwien.prog.command.factory.ICommandFactory;
import at.ac.tuwien.prog.entity.Register;
import at.ac.tuwien.prog.entity.Software;

/**
 * 
 * @author christoph.pickl@student.tuwien.ac.at
 */
public class CompilerImpl implements ICompiler {
	
	private static final Log LOG = LogFactory.getLog(CompilerImpl.class);

	private final ICommandFactory factory;
	
	public CompilerImpl(final ICommandFactory factory) {
		this.factory = factory;
	}

	public byte[] compileSoftware(final Software software) {
		LOG.debug("compileSoftware(software="+software+")");
		
		final int commandsCount = software.getCommands().size();
		final byte[] result = new byte[commandsCount * 4];
		
		for (int i = 0; i < commandsCount; i++) {
			
			final ICommand commandInstance = software.getCommands().get(i);
			final Register commandRegister = this.compileSingleCommand(commandInstance);
			final byte[] commandBytes = commandRegister.toBytes();
//			LOG.trace((i+1)+". commandBytes: " + commandRegister.toBitString());
			
			for (int j = 0; j < commandBytes.length; j++) {
				result[i * 4 + j] = commandBytes[j];
			}
		}
		LOG.trace("result: " + Util.toBitString(result));
		
		return result;
	}
	

	public Register compileSingleCommand(final ICommand command) {
		final int cmdId = this.factory.mapIdByClass(command.getClass()); // CommandConstants.mapCommandToId(command);
		
		int result = 0;
		result |= cmdId;
		
		result <<= CommandConstants.OPERAND2_LENGTH;
		result |= command.getOperand2();
		
		result <<= CommandConstants.OPERAND1_LENGTH;
		result |= command.getOperand1();
		
		return Register.newByInt(result);
	}
	


	/*
	
	final int number = 734234232;
	{
		final int maskOperand1 = createBitMask(0, 14);
		final int maskOperand2 = createBitMask(14, 28);
		final int maskOperation = createBitMask(28, 32);
		final int operand1 = number & maskOperand1; // >> 0
		final int operand2 = (number & maskOperand2) >> 14;
		final int operationId = (number & maskOperation) >> 28;
		ystem.out.println("number:       " + StringUtil.toBitSequence(number));
		ystem.out.println("maskOperand1: " + StringUtil.toBitSequence(maskOperand1)  + " => " + StringUtil.toBitSequence(operand1) + " ("+operand1+")");
		ystem.out.println("maskOperand2: " + StringUtil.toBitSequence(maskOperand2)  + " => " + StringUtil.toBitSequence(operand2) + " ("+operand2+")");
		ystem.out.println("operationId:  " + StringUtil.toBitSequence(maskOperation) + " => " + StringUtil.toBitSequence(operationId) + " ("+operationId+")");
	}
	number:       00101011110000111000011001111000
	maskOperand1: 00000000000000000011111111111111 => 00000000000000000000011001111000 (1656)
	maskOperand2: 00001111111111111100000000000000 => 00000000000000000010111100001110 (12046)
	operationId:  11110000000000000000000000000000 => 00000000000000000000000000000010 (2)
	operation: 2
	operand1: 1656
	operand2: 12046

	*/

}
